﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;

namespace Flagwind.Data
{
	/// <summary>
	/// 辅助类。
	/// </summary>
	internal static class Utility
	{
		#region 私有字段

		private readonly static Dictionary<string, string> _entitySqlMapCache = new Dictionary<string, string>();

		#endregion

		#region 公共属性

		/// <summary>
		/// SqlMap 实体类中映射的 SQL 缓存，键为实体类的 SQL 查询名称。
		/// </summary>
		public static Dictionary<string, string> EntitySqlMapCache
		{
			get
			{
				return _entitySqlMapCache;
			}
		}

		#endregion

		#region 路径替换

		/// <summary>
		/// 替换 Web 路径格式中的相对虚拟路径（~） 为当前程序执行的绝对路径。
		/// </summary>
		/// <param name="path">虚拟路径。</param>
		/// <returns>替换后的绝对路径。</returns>
		public static string ReplaceWebRootPath(string path)
		{
			string rootPath;
			var escapedCodeBase = Assembly.GetExecutingAssembly().EscapedCodeBase;
			var directory = Path.GetDirectoryName(new Uri(escapedCodeBase).LocalPath);
			if(directory != null && (directory.Length > 4 && directory.EndsWith("bin", StringComparison.OrdinalIgnoreCase)))
			{
				// 去除Web项目的 \bin，获取根目录
				rootPath = directory.Substring(0, directory.Length - 3);
			}
			else
			{
				rootPath = "./";
			}
			var buffer = Regex.Replace(path, @"^\s*~[\\/]", rootPath);
			buffer = Regex.Replace(buffer, @"data source\s*=\s*~[\\/]", "Data Source=" + rootPath, RegexOptions.IgnoreCase);
			return buffer;
		}

		#endregion

		#region SQL 批量复制

		/// <summary>
		/// SQL批量复制。
		/// </summary>
		/// <param name="sourceReader">数据源的 DataReader。</param>
		/// <param name="connectionString">目标数据库的连接字符串。</param>
		/// <param name="destinationTableName">要导入的目标表名称。</param>
		/// <param name="batchSize">每次批量处理的大小。</param>
		public static void BatchCopySql(IDataReader sourceReader, string connectionString, string destinationTableName, int batchSize)
		{
			// 目的 
			using(SqlConnection destinationConnection = new SqlConnection(connectionString))
			{
				// 打开连接 
				destinationConnection.Open();
				using(SqlBulkCopy bulkCopy = new SqlBulkCopy(destinationConnection))
				{
					bulkCopy.BatchSize = batchSize;
					bulkCopy.DestinationTableName = destinationTableName;
					bulkCopy.WriteToServer(sourceReader);
				}
			}
			sourceReader.Close();
		}

		/// <summary>
		/// SQL批量复制。
		/// </summary>
		/// <param name="sourceTable">数据源表。</param>
		/// <param name="connectionString">目标数据库的连接字符串。</param>
		/// <param name="destinationTableName">要导入的目标表名称。</param>
		/// <param name="batchSize">每次批量处理的大小。</param>
		public static void BatchCopySql(DataTable sourceTable, string connectionString, string destinationTableName, int batchSize)
		{
			using(SqlConnection destinationConnection = new SqlConnection(connectionString))
			{
				// 打开连接 
				destinationConnection.Open();
				using(SqlBulkCopy bulkCopy = new SqlBulkCopy(destinationConnection))
				{
					bulkCopy.BatchSize = batchSize;
					bulkCopy.DestinationTableName = destinationTableName;
					bulkCopy.WriteToServer(sourceTable);
				}
			}
		}

		#endregion

		#region 获取程序集资源

		/// <summary>
		/// 从指定的程序集中获取一个嵌入式资源的文本内容。
		/// </summary>
		/// <param name="assemblyName">程序集名称。</param>
		/// <param name="resourceName">资源名称。</param>
		/// <returns>资源内容。</returns>
		public static string GetAssemblyResource(string assemblyName, string resourceName)
		{
			var assembly = Assembly.Load(assemblyName);
			return GetAssemblyResource(assembly, resourceName);
		}

		/// <summary>
		/// 根据指定的类型，从该类型所在的程序集中获取指定的资源内容文本。
		/// </summary>
		/// <param name="resourceType">与指定的类型。</param>
		/// <param name="resourceName">资源名称。</param>
		/// <returns>资源内容。</returns>
		public static string GetAssemblyResource(Type resourceType, string resourceName)
		{
			var assembly = resourceType.Assembly;
			resourceName = resourceType.Namespace + "." + resourceName;
			return GetAssemblyResource(assembly, resourceName);
		}

		/// <summary>
		/// 在指定的程序集中寻找指定嵌入式资源文本内容。
		/// </summary>
		/// <param name="assembly">资源所在的程序集。</param>
		/// <param name="resourceName">资源的名称。</param>
		/// <returns>嵌入式资源文本内容。</returns>
		public static string GetAssemblyResource(Assembly assembly, string resourceName)
		{
			string result;
			using(var stream = assembly.GetManifestResourceStream(resourceName))
			{
				if(stream == null)
				{
					throw new ArgumentException(string.Format("未找到指定的嵌入式资源:{0}", resourceName));
				}
				using(StreamReader reader = new StreamReader(stream))
				{
					result = reader.ReadToEnd();
				}
			}

			return result;
		}

		#endregion

		#region 数据类型转换

		/// <summary>
		/// 泛型类型转换方法。
		/// </summary>
		/// <example>
		///  object o1 = 111;
		/// int i = getProperty《int》(o1);
		/// o1 = DBNull.Value;
		/// i = getProperty《int》(o1);
		/// DateTime d = getProperty《DateTime》(o1);
		/// o1 = 123.33m;
		/// double db = getProperty《double》(o1);
		/// o1 = "123.4";
		/// float f = getProperty《float》(o1);
		/// o1 = null;
		/// f = getProperty《float》(o1);
		/// </example>
		/// <typeparam name="T">要转换的目标类型。</typeparam>
		/// <param name="value">Object类型的待转换对象。</param>
		/// <returns>目标类型。</returns>
		public static T ConvertType<T>(object value)
		{
			if(value is T)
			{
				return (T)value;
			}
			else if(value == DBNull.Value || value == null)
			{
				if(typeof(T) == typeof(DateTime))
				{
					//如果取日期类型的默认值 0001/01/01 ,在WCF JSON序列化的时候，会失败。
					object o = new DateTime(1900, 1, 1);
					return (T)o;
				}
				else
				{
					return default(T);
				}
			}
			else
			{
				//如果 value 为 decimal 类型，T 为double 类型，(T)value 将发生错误
				//支持枚举类型
				if(typeof(T).IsEnum)
				{
					return (T)Convert.ChangeType(value, System.TypeCode.Int32);
				}
				else if(!typeof(T).IsGenericType)
				{
					return (T)Convert.ChangeType(value, typeof(T));
				}
				else
				{
					//增加对可空类型的支持
					Type genericTypeDefinition = typeof(T).GetGenericTypeDefinition();
					if(genericTypeDefinition == typeof(Nullable<>))
					{
						if(string.IsNullOrEmpty(value.ToString()))
						{
							return default(T);
						}
						return (T)Convert.ChangeType(value, Nullable.GetUnderlyingType(typeof(T)));
					}
					return (T)Convert.ChangeType(value, typeof(T));
				}
			}
		}

		/// <summary>
		/// Type 转换成 DbType。
		/// </summary>
		/// <param name="type">.NET 数据类型。</param>
		/// <returns>数据库类型。</returns>
		public static DbType ConvertToDbType(Type type)
		{
			DbType result;
			try
			{
				result = (DbType)Enum.Parse(typeof(DbType), type.Name);
			}
			catch
			{
				result = DbType.Object;
			}
			return result;
		}

		/// <summary>
		/// DbType 转换成 Type。
		/// </summary>
		/// <param name="type">数据库类型。</param>
		/// <returns>.NET 数据类型。</returns>
		public static Type ConvertToType(DbType type)
		{
			var result = typeof(DBNull);
			switch(type)
			{
				case DbType.UInt64:
					result = typeof(UInt64);
					break;
				case DbType.Int64:
					result = typeof(Int64);
					break;
				case DbType.Int32:
					result = typeof(Int32);
					break;
				case DbType.UInt32:
					result = typeof(UInt32);
					break;
				case DbType.Single:
					result = typeof(float);
					break;
				case DbType.Date:
				case DbType.DateTime:
				case DbType.Time:
					result = typeof(DateTime);
					break;
				case DbType.String:
				case DbType.StringFixedLength:
				case DbType.AnsiString:
				case DbType.AnsiStringFixedLength:
					result = typeof(string);
					break;
				case DbType.UInt16:
					result = typeof(UInt16);
					break;
				case DbType.Int16:
					result = typeof(Int16);
					break;
				case DbType.SByte:
					result = typeof(byte);
					break;
				case DbType.Object:
					result = typeof(object);
					break;
				case DbType.VarNumeric:
				case DbType.Decimal:
					result = typeof(decimal);
					break;
				case DbType.Currency:
					result = typeof(double);
					break;
				case DbType.Binary:
					result = typeof(byte[]);
					break;
				case DbType.Double:
					result = typeof(Double);
					break;
				case DbType.Guid:
					result = typeof(Guid);
					break;
				case DbType.Boolean:
					result = typeof(bool);
					break;
			}
			return result;
		}

		#endregion

		#region SQL 处理

		/// <summary>
		/// 为字段名加上中括号，避免字段名中有空格的问题。
		/// </summary>
		/// <param name="fields">字段名称数组。</param>
		/// <returns>新的字段名数组。</returns>
		public static string[] PrepareSqlFields(string[] fields)
		{
			string[] result = new string[fields.Length];
			for(int i = 0; i < fields.Length; i++)
			{
				result[i] = "[" + fields[i] + "]";
			}
			return result;
		}

		#endregion

		#region 字符串处理

		/// <summary>
		/// 从输入字符串中寻找一个单词，忽略前面的空白字符，直到遇到单词之后第一个空白字符或者分割符或者标点符号为止。
		/// </summary>
		/// <param name="inputString">输入的字符串</param>
		/// <param name="startIndex">在输入字符串中要寻找单词的起始位置</param>
		/// <param name="maxLength">单词的最大长度，忽略超出部分</param>
		/// <returns>找到的新单词</returns>
		public static string FindWords(string inputString, int startIndex, int maxLength)
		{
			maxLength = maxLength > inputString.Length ? inputString.Length : maxLength;
			bool start = false;
			char[] words = new char[maxLength]; //存储过程名字，最大长度255；
			int index = 0;
			foreach(char c in inputString.ToCharArray(startIndex, maxLength))
			{
				if(Char.IsWhiteSpace(c))
				{
					if(!start)
					{
						continue; //过滤前面的空白字符
					}
					else
					{
						break; //已经获取过字母字符，又遇到了空白字符，说明单词已经结束，跳出。
					}
				}
				else
				{
					if(Char.IsSeparator(c) || Char.IsPunctuation(c))
					{
						if(c == '.' || c == '_' || c == '-' || c == '[' || c == ']')
						{
							words[index++] = c; //放入字母，找单词
						}
						else
						{
							break; //分割符或者标点符号，跳出。
						}
					}
					else
					{
						words[index++] = c; //放入字母，找单词
					}
					if(!start)
					{
						start = true;
					}
				}
			}
			return new string(words, 0, index);
		}

		#endregion
	}
}